﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Repository.cs" company="emrecoskun.net">
//   ecblog.codeplex.com - emrecoskun.net
// </copyright>
// <summary>
//   The base repository
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace EcBlog.Data.Sql.Base
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Entity;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Security.Principal;
    using System.Threading;

    using EcBlog.Data.Base;

    /// <summary>
    /// The base repository
    /// </summary>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        /// <summary>
        /// The context.
        /// </summary>
        private readonly TransactionalDbContext context;

        /// <summary>
        /// The entity set.
        /// </summary>
        private readonly DbSet<TEntity> entitySet;

        /// <summary>
        /// Initializes a new instance of the <see cref="Repository&lt;TEntity&gt;"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        protected Repository(TransactionalDbContext context)
        {
            this.context = context;
            this.entitySet = context.Set<TEntity>();
        }

        /// <summary>
        /// Gets the principal.
        /// </summary>
        protected IPrincipal Principal
        {
            get
            {
                return Thread.CurrentPrincipal;
            }
        }

        /// <summary>
        /// Gets the entity query with specified include properties.
        /// </summary>
        /// <param name="includeProperties">The include properties.</param>
        /// <returns>The entity query</returns>
        public virtual IQueryable<TEntity> Get(string includeProperties = "")
        {
            IQueryable<TEntity> query = this.entitySet;

            query = !string.IsNullOrEmpty(includeProperties)
                        ? includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                            .Aggregate(query, (current, includeProperty) => current.Include(includeProperty))
                        : query;
            return query;
        }

        /// <summary>
        /// Gets the entity query with specified include properties.
        /// </summary>
        /// <param name="includeProperties">The include properties.</param>
        /// <returns>The entity query</returns>
        public IQueryable<TEntity> Get(params Expression<Func<TEntity, object>>[] includeProperties)
        {
            return includeProperties.Aggregate<Expression<Func<TEntity, object>>, IQueryable<TEntity>>(
                this.entitySet, (current, includeProperty) => current.Include(includeProperty));
        }

        /// <summary>
        /// Gets the entity by id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns>The entity</returns>
        public virtual TEntity GetById(object id)
        {
            return this.entitySet.Find(id);
        }

        /// <summary>
        /// Creates the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public virtual void Create(TEntity entity)
        {
            SetLastUpdate(entity);
            this.entitySet.Add(entity);
        }

        /// <summary>
        /// Deletes the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        public virtual void Delete(object id)
        {
            var entityToDelete = this.entitySet.Find(id);
            this.Delete(entityToDelete);
        }

        /// <summary>
        /// Deletes the specified entity to delete.
        /// </summary>
        /// <param name="entityToDelete">The entity to delete.</param>
        public virtual void Delete(TEntity entityToDelete)
        {
            if (this.context.Entry(entityToDelete).State == EntityState.Detached)
            {
                this.entitySet.Attach(entityToDelete);
            }

            this.entitySet.Remove(entityToDelete);
        }

        /// <summary>
        /// Updates the specified entity to update.
        /// </summary>
        /// <param name="entityToUpdate">The entity to update.</param>
        public virtual void Update(TEntity entityToUpdate)
        {
            SetLastUpdate(entityToUpdate);
            this.entitySet.Attach(entityToUpdate);
            this.context.Entry(entityToUpdate).State = EntityState.Modified;
        }

        /// <summary>
        /// Gets the entity list with raw SQL.
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns>The entity list</returns>
        public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
        {
            return this.entitySet.SqlQuery(query, parameters).ToList();
        }

        /// <summary>
        /// Sets the last update.
        /// </summary>
        /// <typeparam name="T">The type of entity</typeparam>
        /// <param name="entity">The entity.</param>
        protected static void SetLastUpdate<T>(T entity)
        {
            var property = entity.GetType().GetProperty("LastModifiedUser");
            if (property != null)
            {
                property.SetValue(entity, Thread.CurrentPrincipal.Identity.Name, null);
            }

            property = entity.GetType().GetProperty("LastModified");
            if (property != null)
            {
                property.SetValue(entity, DateTime.Now, null);
            }
        }
    }
}